package hu.mapro.gwtui.gxt.client.recordseditor.requestfactory.valuelist;

import hu.mapro.gwtui.client.Action;
import hu.mapro.gwtui.gxt.client.recordseditor.requestfactory.Interface;
import hu.mapro.gwtui.gxt.client.recordseditor.requestfactory.RequestContextHolderInterface;
import hu.mapro.model.Getter;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.google.common.collect.ImmutableList;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.RequestContext;

public class ValueCollectionAdapter<T extends BaseProxy, V extends BaseProxy, RC extends RequestContext> implements Interface<T>  {

	RequestContextHolderInterface<RC> requestContextHolder;
	ValueDataCache<V> cache;
	Getter<V, ? extends Collection<T>> collectionProperty;
	ValueDataSaverInterface<V, RC> saver;

	public ValueCollectionAdapter(
			RequestContextHolderInterface<RC> requestContextHolder,
			ValueDataCache<V> cache,
			Getter<V, ? extends Collection<T>> collectionProperty,
			ValueDataSaverInterface<V, RC> saver) {
		super();
		this.requestContextHolder = requestContextHolder;
		this.cache = cache;
		this.collectionProperty = collectionProperty;
		this.saver = saver;
	}

	@Override
	public RequestContext newRequestContext() {
		requestContextHolder.newRequestContext();
		return requestContextHolder.getCurrentRequestContext();
	}

	@Override
	public void list(final Receiver<List<T>> receiver) {
		cache.fireWhenLoaded(new Receiver<V>() {
			@Override
			public void onSuccess(V response) {
				receiver.onSuccess(ImmutableList.copyOf(collectionProperty.get(response)));
			}
		});
	}

	@Override
	public void persist(final T object, final Receiver<Void> receiver) {
		cache.fireWhenLoaded(new Receiver<V>() {
			@Override
			public void onSuccess(V userData) {
				collectionProperty.get(userData).add(object);
				saver.saveData(userData, requestContextHolder.getCurrentRequestContext(), receiver);
			}
		});
	}

	@Override
	public void merge(final T object, final Receiver<T> receiver) {
		cache.fireWhenLoaded(new Receiver<V>() {
			@Override
			public void onSuccess(V userData) {
				final T edited = requestContextHolder.getCurrentRequestContext().edit(object);
				
				Iterator<T> iterator = collectionProperty.get(userData).iterator();
				
				while (iterator.hasNext()) {
					T oldEdited = iterator.next();
					
					if (oldEdited==edited) {
						iterator.remove();
						collectionProperty.get(userData).add(edited);

						saver.saveData(userData, requestContextHolder.getCurrentRequestContext(), new Receiver<Void>() {
							@Override
							public void onSuccess(Void response) {
								receiver.onSuccess(edited);
							}
						});
						
						return;
					}
					
				}

				throw new RuntimeException("area was not in list");
			}
		});
	}

	@Override
	public void remove(final List<T> objects, final Receiver<Void> receiver) {
		cache.fireWhenLoaded(new Receiver<V>() {
			@Override
			public void onSuccess(V userData) {
				mainLoop : for (T object : objects) {
					final T edited = requestContextHolder.getCurrentRequestContext().edit(object);
					
					Iterator<T> iterator = collectionProperty.get(userData).iterator();
					
					while (iterator.hasNext()) {
						T oldEdited = iterator.next();
						
						if (oldEdited==edited) {
							iterator.remove();
							continue mainLoop;
						}
					}
					
					throw new RuntimeException("area was not in list");
					
				}
				
			saver.saveData(userData, requestContextHolder.getCurrentRequestContext(), receiver);
			}
		});
	}

	@Override
	public void addChangeHandler(Action handler) {
		cache.getUpdateHandlers().add(handler);
	}
	
}
